﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//zdm 2019.6.12
//根据随机数种子生成随机数
public class RandomGenerator : MonoBehaviour
{
    //单例
    public static RandomGenerator Instance
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = GameObject.FindObjectOfType<RandomGenerator>();
            }
            if (!_Instance)
            {
                GameObject obj = new GameObject("RandomGenerator");
                _Instance = obj.AddComponent<RandomGenerator>();
            }
            return _Instance;
        }
        set
        {
            _Instance = value;
        }
    }
    private static RandomGenerator _Instance;

    //参数
    //不要问是干什么的，我也不知道，反正能用就完事了
    static int n = 624, m = 397, r = 31, s = 7, t = 15, u = 11, l = 18;
    static uint a = 0x9908B0DF, f = 1812433253, b = 0x9D2C5680, c = 0xEFC60000;
    static uint MASK_LOWER = (1u << r) - 1, MASK_UPPER = (1u << r);
    uint[] mt = new uint[n + 1];
    int index;

    //初始化随机数种子，原理不懂，反正能用
    public void SetSeed(uint sd)
    {
        mt[0] = sd;
        for (int i = 1; i < n; i++)
        {
            mt[i] = (uint)(f * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
        }
        index = n;
    }

    private void Twist()
    {
        uint i, x, xA;
        for (i = 0; i < n; i++)
        {
            x = (uint)(mt[i] & MASK_UPPER) + (mt[(i + 1) % n] & MASK_LOWER);
            xA = x >> 1;
            if ((x & 0x1) != 0)
            {
                xA ^= a;
            }
            mt[i] = mt[(i + m) % n] ^ xA;
        }

        index = 0;
    }

    //获取一个随机数，以类似于c#里random的格式进行调用
    public int Next()
    {
        uint y;
        int i = index;
        if (index >= n)
        {
            Twist();
            i = index;
        }
        y = mt[i];
        index = i + 1;
        y ^= (y >> u);
        y ^= (y << s) & b;
        y ^= (y << t) & c;
        y ^= (y >> l);
        return (int)y;
    }

    //两种获取随机数的方式，获取小于x的整数，获取[l,r)区间内的整数
    public int Next(int x)
    {
        if (x <= 0)
            return 0;
        return ((Next() % x) + x) % x;
    }

    public int Next(int l, int r)
    {
        return Next(r - l) + l;
    }

    private static float zoom = 10000f;

    public float Next(float x)
    {
        if (x <= 0)
            return 0;
        int p = (int)(x * zoom);
        return Next(p) / zoom;
    }

    public float Next(float l, float r)
    {
        return Next(r - l) + l;
    }

    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
        SetSeed((uint)(System.DateTime.Now.Ticks));
    }
    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
